package cn.tedu.csmall.product.config;

import cn.tedu.csmall.product.filter.JwtAuthorizationFilter;
import cn.tedu.csmall.product.web.JsonResult;
import cn.tedu.csmall.product.web.ServiceCode;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.firewall.StrictHttpFirewall;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * Security配置类
 *
 * @author java@tedu.cn
 * @version 0.0.1
 */
@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
    @Autowired
    JwtAuthorizationFilter jwtAuthorizationFilter;

    public SecurityConfiguration() {
        System.out.println("创建配置类：SecurityConfiguration");
    }

    /**
     * 解决请求头中的cookie包含有中文字符，而spring security的默认字符集不识别中文，导致验证报错
     */
/*    @Bean
    public StrictHttpFirewall httpFirewall() {
        StrictHttpFirewall firewall = new StrictHttpFirewall();
        firewall.setAllowedHeaderNames((header) -> true);
        firewall.setAllowedHeaderValues((header) -> true);
        firewall.setAllowedParameterNames((parameter) -> true);
        return firewall;
    }*/
    /**
     * 设置未登录拦截的网址类型
     * 设置是否开启跨域防护
     * 设置是否跳转到默认的登录页
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /*http.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
            @Override
            public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
                String message = "未檢測到登錄信息，請登錄！（開發過程中請檢查請求頭中是否携帶了有效的Jwt）"  ;
                JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_UNAUTHORIZED,message);
                response.setContentType("application/json;charset=utf-8");
                PrintWriter writer = response.getWriter();
                writer.println(JSON.toJSONString(jsonResult));
                writer.close();
            }
        });*/

        // 【配置白名单】
        // 在配置路径时，星号是通配符
        // 1个星号只能匹配任何文件夹或文件的名称，但不能跨多个层级
        // 例如：/*/test.js，可以匹配到 /a/test.js 和 /b/test.js，但不可以匹配到 /a/b/test.js
        // 2个连续的星号可以匹配若干个文件夹的层级
        // 例如：/**/test.js，可以匹配 /a/test.js 和 /b/test.js 和 /a/b/test.js

        String[] urls = {
                "/doc.html",
                "/**/*.js",
                "/**/*.css",
                "/swagger-resources",
                "/v2/api-docs"
        };

        http.csrf().disable();//禁用CSRF（防止伪造的跨域攻击），默认是开启CSRF，导致所有的post请求无法访问

        //第一种方法解决预检
//        http.cors(); // 启用Spring Security框架的处理跨域的过滤器，此过滤器将放行跨域请求，包括预检的OPTIONS请求

        http.authorizeRequests() // 对请求执行认证与授权
//                 .antMatchers(urls) // 匹配某些请求路径，必须严格匹配路径
                .mvcMatchers(urls) // 匹配某些请求路径，匹配路径时不关心扩展名，例如配置的路径是 /admin，则可以匹配上 /admin、/admin.html、/admin.jpg
                .permitAll() // （对此前匹配的请求路径）不需要通过认证即允许访问
                //第二种方法解决预检，允许OPTIONS类型请求未认证就能访问
                .antMatchers(HttpMethod.OPTIONS,"/**")
                .permitAll()
                .anyRequest() // 除以上配置过的请求路径以外的所有请求路径
                .authenticated(); // 要求是已经通过认证的

//        http.formLogin();// 开启表单验证，即视为未通过认证时，将重定向到登录表单，如果无此配置，则直接响应403

        //必须放在UsernamePasswordAuthenticationFilter过滤器之前才有效，
        // 要先存入SecurityContext中，后续的过滤器会检查SecurityContext内容，判断是否登录。
        http.addFilterBefore(jwtAuthorizationFilter,UsernamePasswordAuthenticationFilter.class);
    }

}